/*
 *  Copright: 2008 Philipp Leitner, Distributed Systems Group,
 *            TU Wien, leitner@infosys.tuwien.ac.at
 *
 *  This file is part of DAIOS.
 *
 *  DAIOS is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation.
 * 
 *  DAIOS is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with DAIOS.  If not, see
 *  <http://www.gnu.org/licenses/>.
 * 
 */

package at.ac.tuwien.infosys.dsg.daiosPlugins.nativeInvoker.backend;

import java.io.InputStream;
import java.net.URL;
import java.util.Hashtable;
import java.util.logging.Logger;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;

import org.apache.axiom.om.OMElement;
import org.jaxen.JaxenException;

import at.ac.tuwien.infosys.dsg.daios.exceptions.InvocationException;
import at.ac.tuwien.infosys.dsg.daios.exceptions.SOAPEncodedException;

/**
 * This is a minimalistic SOAP stack. It is trimmed towards the
 * specific needs of the Daios framework.
 * 
 * @author philipp
 *
 */
public class SOAPBackend {
	
	private Logger log = null;
	private HTTPBackend http = null;
	private Hashtable<QName, String> headers = null;
	private SOAPEncoder encoder = null;
	
	/**
	 * Construct a new SOAP invocation backend.
	 * 
	 * @param endpoint The endpoint that should be targeted by the
	 * SOAP invocation.
	 * @param log The logger to use.
	 * @param headers A table of SOAP headers that should be set in
	 * the SOAP invocation. This allows the user to set specific SOAP
	 * headers if necessary.
	 * @param httpHeaders A table of HTTP headers that should be set
	 * in addition to the default headers. This allows the user to set
	 * specific HTTP request headers if necessary.
	 */
	public SOAPBackend(URL endpoint, Logger log,
			Hashtable<QName, String> headers, Hashtable<String, String> httpHeaders) {
		this.log = log;
		this.headers = headers;
		this.http = new HTTPBackend(endpoint, log, httpHeaders, true);
		this.encoder = SOAPEncoder.getInstance();
	}
	
	/**
	 * Do a SOAP invocation to the endpoint associated with this
	 * SOAP backend.
	 * 
	 * @param body The XML content that should be transferred (as
	 * body) with this SOAP invocation.
	 * @return The SOAP response, i.e. the XML body of the response
	 * as well as the SOAP response headers.
	 * 
	 * @throws InvocationException
	 */
	public SOAPResponse invoke(OMElement body)
		throws InvocationException {
		
		// SOAP-encode body
		body = soapEncode(body);
		
		// use HTTPBackend to transfer body and receive result
		InputStream response = null;;
		response = 
			http.httpPostInvocation(body);
		
		// SOAP-decode response
		SOAPResponse soapResponse = null;
		try {
			soapResponse = soapDecode(response,
					http.getHTTPResponseHeaders());
		} catch (SOAPEncodedException e) {
			throwInvocationException(e);
		} catch (JaxenException e) {
			throwInvocationException(e);
		} catch (XMLStreamException e) {
			throwInvocationException(e);
		}
		
		// return
		return soapResponse;
		
	}
	
	/**
	 * Do a SOAP invocation to the endpoint associated with this
	 * SOAP backend, but do not return any response and do not
	 * report errors.
	 * 
	 * @param body The XML content that should be transferred (as
	 * body) with this SOAP invocation.
	 * 
	 * @throws InvocationException
	 */	
	public void invokeOneway(OMElement body) throws InvocationException {
		
		// SOAP-encode body
		body = soapEncode(body); 
		
		// use HTTPBackend to transfer body and receive result
		http.httpPostInvocationOneway(body);
		
	}
	
	/**
	 * SOAP-encode the body (i.e. add SOAP envelope).
	 * 
	 * @param body The XML body.
	 * @return The SOAP message (in XML representation).
	 */
	private OMElement soapEncode(OMElement body) {
		return encoder.soapEncode(body, headers);
	}
	
	/**
	 * SOAP-decode a message in XML representation.
	 * 
	 * @param encoded The SOAP message as String.
	 * @param httpHeaders The HTTP response headers to
	 * set in the SOAPResponse.
	 * @return The SOAPResponse containing the SOAP response
	 * body, a table of SOAP response headers and another
	 * table of HTTP response headers.
	 * 
	 * @throws SOAPEncodedException
	 * @throws JaxenException
	 * @throws XMLStreamException
	 */
	private SOAPResponse soapDecode(InputStream encoded,
			Hashtable<String, String> httpHeaders)
			throws SOAPEncodedException, JaxenException, XMLStreamException {
		
		return encoder.soapDecode(encoded, httpHeaders);
		
	}
	
	/**
	 * There was an error while processing - throw an InvocationException.
	 * 
	 * @param e The thrown error.
	 * @throws InvocationException
	 */
	private void throwInvocationException(Exception e)
		throws InvocationException {
		log.severe("Exception thrown during Daios processing:");
		e.printStackTrace();
		throw new InvocationException(e);
		
	}
	
}
